package com.dctp.common.service.simulate;

import com.alibaba.fastjson.JSONObject;
import com.dctp.common.core.RedisManager;
import com.dctp.common.vo.OrderEventType;
import com.dctp.common.vo.ProfitStat;
import com.dctp.common.vo.RedisKey;
import com.jfinal.plugin.activerecord.Record;
import com.dctp.common.cache.ContractCache;
import com.dctp.common.cache.FuturesCache;
import com.dctp.common.cache.FxRateCache;
import com.dctp.common.cache.simulate.SimulateProfitStatCache;
import com.dctp.common.utils.DateUtil;
import com.dctp.common.vo.simulate.SimulateProfitStat;
import com.jfinal.plugin.activerecord.Db;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import static com.dctp.common.vo.TipCode.*;

public class SimulateTradeService {

    private static final Logger logger = LoggerFactory.getLogger(SimulateTradeService.class);

    // 线程池和队列（可根据业务量调整参数）
    private static final ThreadPoolExecutor FUND_FLOW_EXECUTOR = new ThreadPoolExecutor(
            2, 8, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10000));

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(FUND_FLOW_EXECUTOR::shutdown)); // #9
    }


   

    /**
     * 计算盈亏（按tick_size和tick_value）
     * 
     * @param direction  方向 1=买多, 2=卖空
     * @param openPrice  开仓价
     * @param closePrice 平仓价
     * @param quantity   手数
     * @return 盈亏金额
     */
    public static BigDecimal calculatePnl(int direction, BigDecimal openPrice, BigDecimal closePrice, int quantity,
            Long futuresId) {

                
        // 获取Future对应的currency
        Record futures = FuturesCache.get(futuresId);
        if (futures == null) {
            return BigDecimal.ZERO;
        }

       
        BigDecimal tickSize = futures.getBigDecimal("tick_size");
         
        BigDecimal tickValue = futures.getBigDecimal("tick_value");
        if (openPrice == null || closePrice == null
                || tickSize == null || tickValue == null
                || tickSize.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }

        String currency = futures.getStr("currency");
        // 获取汇率，没设置默认返回1
        BigDecimal rate = FxRateCache.getRateByCodeAndTarget(currency, "HKDT");
        BigDecimal diff = (direction == 1)
                ? closePrice.subtract(openPrice)
                : openPrice.subtract(closePrice);
        BigDecimal ticks = diff.divide(tickSize, 8, RoundingMode.HALF_UP);

        // logger.info("开仓价：{},平仓价：{},结算价差：{} ----->  {}ticks * {}每点价值 * {}手 * {}汇率 = {}",
        //         openPrice,closePrice,diff,ticks, tickValue, quantity, rate, ticks.multiply(tickValue).multiply(BigDecimal.valueOf(quantity)).multiply(rate));
         return ticks.multiply(tickValue)
                .multiply(BigDecimal.valueOf(quantity)) // #10
                .multiply(rate) // #11 汇率转换
                .setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 封装下单的扣余额和写订单逻辑，成功返回订单Record，否则抛异常
     */
    public static Record handleOrder(Record user, Record contract, int direction, int orderSource,
            int quantity, BigDecimal openPrice, BigDecimal userInputPrice, BigDecimal marginOccupied,
            BigDecimal openFee) {
        Long userId = user.getLong("id");

        Long futuresId = contract.getLong("futures_id");
        String symbol = contract.getStr("symbol");
        BigDecimal lastPrice = ContractCache.getLastPriceBySymbol(symbol);
        if (lastPrice == null) {
            logger.error("无法获取最新价格，symbol: {}", symbol);
            throw new RuntimeException(OPEN_FAIL);
        }
        if (null == openPrice) {
            openPrice = lastPrice;
        }
        // 判断是否挂单
        int status = 1; // 默认持仓中
        if (direction == 1) {
            if (userInputPrice != null) {
                if (userInputPrice.compareTo(lastPrice) > 0) {
                    openPrice = lastPrice; // 以最新价成交
                } else {
                    // 挂单
                    status = 0;
                    openPrice = userInputPrice;
                }
            } 
        } else if (direction == 2) {
            if (userInputPrice != null) {
                if (userInputPrice.compareTo(lastPrice) < 0) {
                    openPrice = lastPrice; // 以最新价成交
                } else {
                    // 挂单
                    status = 0;
                    openPrice = userInputPrice;
                }
            }
        }

        Integer orderType = status == 0 ? 2 : 1; // 1=开仓，2=挂单
        BigDecimal totalDeduct = marginOccupied;
        if (status == 1) {
            totalDeduct = totalDeduct.add(openFee); // 只有开仓时才扣除服务费
        }
        int rows = Db.update(
                "UPDATE dctp_user SET simulate_balance = simulate_balance - ? WHERE id = ? AND simulate_balance >= ?",
                totalDeduct, userId, totalDeduct);
        if (rows != 1) {
            throw new RuntimeException(BALANCE_NOT_ENOUGH);
        }

        // 写入订单
        // 注意：status=1 代表已开仓，0=挂单
        Long contractId = contract.getLong("id");
        LocalDateTime now = DateUtil.now();
        SimulateProfitStat profitStat = SimulateProfitStatCache.get(userId);
        BigDecimal initProfit = BigDecimal.ZERO;
        if (null != profitStat) {
            initProfit = profitStat.getInitEquity();
        }
        if (null == initProfit) {
            initProfit = BigDecimal.ZERO;
        }

        Record order = new Record()
                .set("user_id", userId)
                .set("contract_id", contractId)
                .set("futures_id", futuresId)
                .set("symbol", symbol)
                .set("direction", direction)
                .set("order_source", orderSource)
                .set("quantity", quantity)
                .set("open_price", openPrice)
                .set("open_time", DateUtil.currentTimestamp())
                .set("margin_occupied", marginOccupied)
                .set("open_service_fee", openFee)
                .set("status", status)
                .set("order_type", orderType)
                .set("create_time", now)
                .set("update_time", now)
                .set("parent_id", user.getLong("parent_id"))
                .set("init_profit", initProfit)// 初始权益
                .set("bottom_role_user_id", user.getLong("bottom_role_user_id"))
                .set("middle_role_user_id", user.getLong("middle_role_user_id"))
                .set("top_role_user_id", user.getLong("top_role_user_id"));
        if (!Db.save("dctp_simulate_order", order)) {
            throw new RuntimeException(ORDER_SAVE_FAIL);
        }

        if (status == 1) {
            publishOpenOrder(userId,symbol);
        }

        return order;
    }

    /**
     * 下单限仓 / 风控校验
     *
     * @param userId   下单用户 ID
     * @param quantity 本次开仓手数
     * @throws RuntimeException 校验不通过时直接抛异常（外层 @Before(Tx.class) 会自动回滚并可捕获返回前端）
     */
    public static void checkAccountLimit(Long userId, int quantity) {

        // 1. 读取该用户的限仓规则（启用状态）
        Record limit = Db.findFirst(
                "SELECT daily_order_limit, single_order_lot_limit, max_position_lot, " +
                        "       daily_max_open_lot, force_close_times, limit_enabled " +
                        "FROM   dctp_account_limit " +
                        "WHERE  user_id = ? AND limit_enabled = 1",
                userId);

        if (limit == null) {
            return; // 未配置限仓 = 不限
        }

        /* ---------- ① 单笔手数上限 ---------- */
        Integer singleLotLimit = limit.getInt("single_order_lot_limit");
        if (singleLotLimit != null && singleLotLimit > 0 && quantity > singleLotLimit) {
            throw new RuntimeException(SINGLE_LOT_LIMIT);
        }

        /* ---------- ② 当日开仓笔数上限 ---------- */
        Integer dailyOrderLimit = limit.getInt("daily_order_limit");
        if (dailyOrderLimit != null && dailyOrderLimit > 0) {
            Long todayCount = Db.queryLong(
                    "SELECT COUNT(*) FROM dctp_order WHERE user_id=? AND DATE(create_time)=CURDATE()",
                    userId);
            if (todayCount + 1 > dailyOrderLimit) {
                throw new RuntimeException(DAILY_ORDER_LIMIT);
            }
        }

        /* ---------- ③ 当日累计开仓手数 ---------- */
        Integer dailyLotLimit = limit.getInt("daily_max_open_lot");
        if (dailyLotLimit != null && dailyLotLimit > 0) {
            Long todayLots = Db.queryLong(
                    "SELECT IFNULL(SUM(quantity),0) FROM dctp_order " +
                            "WHERE user_id=? AND DATE(create_time)=CURDATE()",
                    userId);
            if (todayLots + quantity > dailyLotLimit) {
                throw new RuntimeException(DAILY_LOT_LIMIT);
            }
        }

        /* ---------- ④ 当前持仓手数上限 ---------- */
        Integer maxPositionLot = limit.getInt("max_position_lot");
        if (maxPositionLot != null && maxPositionLot > 0) {
            Long holdingLots = Db.queryLong(
                    "SELECT IFNULL(SUM(quantity),0) FROM dctp_order " +
                            "WHERE user_id=? AND status=1", // status=1 = 持仓中
                    userId);
            if (holdingLots + quantity > maxPositionLot) {
                throw new RuntimeException(MAX_POSITION_LOT);
            }
        }

        /* ---------- ⑤ 强平时间段禁止开仓 ---------- */
        String forceTimes = limit.getStr("force_close_times"); // 例："14:55,23:30"
        if (forceTimes != null && !forceTimes.trim().isEmpty()) {
            String nowHHmm = LocalDateTime.now()
                    .format(DateTimeFormatter.ofPattern("HH:mm"));
            for (String t : forceTimes.split(",")) {
                if (nowHHmm.equals(t.trim())) {
                    throw new RuntimeException(FORCE_CLOSE_TIME);
                }
            }
        }
    }

    public static String handleCloseOrder(Record order, Record user, BigDecimal closePrice, int closeQuantity,Integer orderType) {

        Long userId = user.getLong("id");
        Long orderId = order.getLong("id");
        BigDecimal marginOccupied = order.getBigDecimal("margin_occupied");
        // 计算实际盈亏
        BigDecimal realizedPnl =
                calculatePnl(order.getInt("direction"),order.getBigDecimal("open_price"),
                        closePrice,closeQuantity,order.getLong("futures_id"));

        // 释放保证金+结算盈亏+扣除平仓手续费
        BigDecimal closeServiceFee = order.getBigDecimal("open_service_fee");
        if (closeServiceFee == null) {
            closeServiceFee = BigDecimal.ZERO;
        }
        BigDecimal refund = marginOccupied.add(realizedPnl).subtract(closeServiceFee);
        BigDecimal userBalance = user.getBigDecimal("simulate_balance");
        if (refund.compareTo(BigDecimal.ZERO) < 0 && refund.abs().compareTo(userBalance) > 0 ) {
            //处理余额为负的问题
            BigDecimal gap = refund.abs().subtract(userBalance);
            refund = userBalance.negate();
            realizedPnl = realizedPnl.add(gap);
            logger.warn("模拟交易穿仓处理，穿仓额度：{},userBalance:{},refund:{},realizedPnl:{}",gap,userBalance,refund,realizedPnl);

        }

        logger.info(
                "平仓参数：userId={}, orderId={}, closePrice={}, closeQuantity={}, closeServiceFee={}, realizedPnl={}, refund={}",
                userId, orderId, closePrice, closeQuantity, closeServiceFee, realizedPnl, refund);

        int updateRows = Db.update("UPDATE dctp_user SET simulate_balance = simulate_balance + ? WHERE id = ?", refund, userId);
        if (updateRows != 1) {
            throw new RuntimeException(REFUND_FAIL);
        }

        LocalDateTime now = DateUtil.now();
        // 更新订单状态
        int orderRows = Db.update(
                "UPDATE dctp_simulate_order SET close_price=?, close_quantity=?, close_time=?, close_service_fee=?, realized_pnl=?,order_type = ?, status=2, update_time=? WHERE id=?",
                closePrice, closeQuantity, now, closeServiceFee, realizedPnl, orderType,now, orderId);
        if (orderRows != 1) {
            throw new RuntimeException(ORDER_UPDATE_FAIL);
        }


        String symbol = order.getStr("symbol");
        Record counts = Db.findFirst(
                "SELECT COUNT(*) AS total_count, IFNULL(SUM(CASE WHEN symbol = ? THEN 1 ELSE 0 END), 0) AS symbol_count " +
                        "FROM dctp_simulate_order WHERE user_id = ? AND status = 1 AND id != ?",
                symbol, userId, orderId);

        int totalCount = counts.getInt("total_count");
        int symbolCount = counts.getInt("symbol_count");

        if (totalCount == 0) {
            // 如果没有剩余持仓，更新用户的动态权益
            SimulateProfitStat profitStat = SimulateProfitStatCache.get(userId);
            if (profitStat != null) {
                profitStat.setTotalPnl(BigDecimal.ZERO);
                profitStat.setDynamicEquity(user.getBigDecimal("simulate_balance").add(refund));
                SimulateProfitStatCache.set(userId, profitStat);
            }
        }
        if (symbolCount == 0 || totalCount == 0) {
            publishCloseOrder(order);
        }

        logger.info("平仓成功，订单ID: {}, 用户ID: {}, 平仓价: {}, 手数: {}, 实现盈亏: {}, 返还资金: {}",
                orderId, userId, closePrice, closeQuantity, realizedPnl, refund);
        return "ok";
    }

    public static void publishCloseOrder(Record order) {

        try {
            Map<String,Object> pubMessage = new HashMap<>();
            pubMessage.put("data", order.toMap());
            pubMessage.put("type", OrderEventType.DELETE);
            RedisManager.getInstance().publish(
                    RedisKey.PUB_SUB_SIMULATE_TRADE_ORDER,
                    JSONObject.toJSONString(pubMessage)
            );
        } catch (Exception e) {
            logger.error("❌ 订单发布Redis消息失败: {}", e.getMessage(), e);
        }
    }


    public static void publishOpenOrder(Long userId,String symbol) {

        try {
            JSONObject data = new JSONObject();
            data.put("symbol", symbol);
            data.put("user_id",userId);
            Map<String,Object> pubMessage = new HashMap<>();
            pubMessage.put("data", data);
            pubMessage.put("type", OrderEventType.ADD);
            RedisManager.getInstance().publish(
                    RedisKey.PUB_SUB_SIMULATE_TRADE_ORDER,
                    JSONObject.toJSONString(pubMessage)
            );
        } catch (Exception e) {
            logger.error("❌ 订单发布Redis消息失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 计算强平价
     * 
     * @param openPrice
     * @param direction
     * @param futuresId
     * @param
     * @return
     */
    public static BigDecimal calFocusClosePrice(BigDecimal openPrice, Integer direction, Long futuresId,
            BigDecimal marginOccupied) {
        Record futures = FuturesCache.get(futuresId);
        if (futures == null) {
            return BigDecimal.ZERO;
        }
        BigDecimal tickSize = futures.getBigDecimal("tick_size");
        BigDecimal tickValue = futures.getBigDecimal("tick_value");
        String currency = futures.getStr("currency");
        BigDecimal rate = FxRateCache.getRateByCodeAndTarget(currency, "HKDT");
        if (openPrice == null || tickSize == null || tickValue == null || tickSize.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        // denominator = 手数 * 每点价值 * 汇率 / tickSize
        BigDecimal denominator = tickValue.multiply(rate).divide(tickSize, 8, RoundingMode.HALF_UP);
        if (denominator.compareTo(BigDecimal.ZERO) == 0)
            return BigDecimal.ZERO;

        BigDecimal offset = marginOccupied.divide(denominator, 8, RoundingMode.HALF_UP);
        if (direction == 1) { // 多单
            return openPrice.subtract(offset).setScale(2, RoundingMode.HALF_UP);
        } else { // 空单
            return openPrice.add(offset).setScale(2, RoundingMode.HALF_UP);
        }
    }
}
